/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package simplemetrosystem.model.Element;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.util.ArrayList;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import simplemetrosystem.control.ElementFactory;
import simplemetrosystem.control.ElementManager;
import simplemetrosystem.model.Element.Interface.CanvasElement;
import simplemetrosystem.model.Element.Interface.ElementListener;
import simplemetrosystem.model.Element.Interface.HighLightable;
import simplemetrosystem.model.Element.Interface.MetroElement;

/**
 *
 * @author Eric Qian
 */
public class MetroEdge implements
        MetroElement, CanvasElement, Comparable<MetroEdge>, HighLightable,
        ElementListener {

    /**
     * 
     * @param stationA
     * @param stationB
     * @param line
     * @param serialNumber 
     * @param distance
     * @param time
     * @param language 
     */
    public MetroEdge(MetroStation stationA, MetroStation stationB, MetroLine line,
            int serialNumber, double distance, double time, boolean language) {
        init(stationA, stationB, line, serialNumber, distance, time, language);
    }

    /**
     * 
     * @param stationA
     * @param stationB
     * @param line
     * @param serialNumber 
     * @param distance
     * @param language 
     */
    public MetroEdge(MetroStation stationA, MetroStation stationB, MetroLine line, int serialNumber,
            double distance, boolean language) {
        init(stationA, stationB, line, serialNumber, distance, 999999, language);
    }

    private void init(MetroStation stationA, MetroStation stationB, MetroLine line,
            int serialNumber, double distance, double time, boolean language) {
        this.stationA = stationA;
        this.stationB = stationB;
        this.line = line;
        this.serialNumber = serialNumber;
        this.distance = distance;
        this.time = time;
        this.language = language;
        this.highLightFlag = false;
        similarEdges = new ArrayList<MetroEdge>();
        similarEdges.add(this);
    }

    /**
     * 
     * @return
     */
    public String getName() {
        return stationA.getName() + "-" + stationB.getName();
    }

    /**
     * 
     * @return
     */
    public String getChineseName() {
        return stationA.getChineseName() + "-" + stationB.getChineseName();
    }

    public String getDisplayName() {
        if (language) {
            return getName();
        } else {
            return getChineseName();
        }
    }

    /**
     * 
     * @param distance
     */
    public void setDistance(double distance) {
        this.distance = distance;
    }

    /**
     * 
     * @param line
     */
    public void setLine(MetroLine line) {
        this.line = line;
    }

    /**
     * 
     * @param time
     */
    public void setTime(double time) {
        this.time = time;
    }

    public void exchangeStation() {
        MetroStation t = stationA;
        stationA = stationB;
        stationB = t;
    }

    /**
     * 
     * @return
     */
    public MetroStation getStationA() {
        return stationA;
    }

    /**
     * 
     * @return
     */
    public MetroStation getStationB() {
        return stationB;
    }

    /**
     * 
     * @return
     */
    public MetroLine getLine() {
        return line;
    }

    /**
     * 
     * @return
     */
    public int getSerialNumber() {
        return serialNumber;
    }

    /**
     * 
     * @param serialNumber
     */
    public void setSerialNumber(int serialNumber) {
        this.serialNumber = serialNumber;
    }

    /**
     * 
     * @return
     */
    public String getEdgeStatus() {
        return line.getStatus();
    }

    /**
     * 
     * @return
     */
    public double getDistance() {
        return distance;
    }

    /**
     * 
     * @return
     */
    public double getTime() {
        return time;
    }

    public int getLogX() {
        int x1 = stationA.getLogX();
        int x2 = stationB.getLogX();
        if (x1 > x2) {
            return x2;
        } else {
            return x1;
        }
    }

    public int getLogY() {
        int y1 = stationA.getLogY();
        int y2 = stationB.getLogY();
        if (y1 > y2) {
            return y2;
        } else {
            return y1;
        }
    }

    public int getWidth() {
        return Math.abs(stationA.getLogX() - stationB.getLogX());
    }

    public int getHeight() {
        return Math.abs(stationA.getLogY() - stationB.getLogY());
    }

    /**
     * 
     * @return
     */
    public int getEdgesSize() {
        return similarEdges.size();
    }

    public int getEdgeLocation() {
        return similarEdges.indexOf(this);
    }

    /**
     * 
     * @param doc
     * @return
     */
    public Element getElement(Document doc) {
        Element edgeElement = doc.createElement(ElementFactory.ROOT_EDGE);

        Element sAElement = doc.createElement(ElementFactory.EDG_STA);
        edgeElement.appendChild(sAElement);
        Text sAText = doc.createTextNode(stationA.getName());
        sAElement.appendChild(sAText);


        Element sBElement = doc.createElement(ElementFactory.EDG_STB);
        edgeElement.appendChild(sBElement);
        Text sBText = doc.createTextNode(stationB.getName());
        sBElement.appendChild(sBText);

        Element lineElement = doc.createElement(ElementFactory.EDG_LIN);
        edgeElement.appendChild(lineElement);
        Text lineText = doc.createTextNode(line.getName());
        lineElement.appendChild(lineText);

        Element serialElement = doc.createElement(ElementFactory.EDG_SERNUM);
        edgeElement.appendChild(serialElement);
        Text serialText = doc.createTextNode("" + serialNumber);
        serialElement.appendChild(serialText);

        Element disElement = doc.createElement(ElementFactory.EDG_DIS);
        edgeElement.appendChild(disElement);
        Text disText = doc.createTextNode("" + distance);
        disElement.appendChild(disText);

        Element timElement = doc.createElement(ElementFactory.EDG_TIM);
        edgeElement.appendChild(timElement);
        Text timText = doc.createTextNode("" + time);
        timElement.appendChild(timText);

        return edgeElement;
    }

    @Override
    public String toString() {

        return "<edge>" + "\n" +
                "<stationA>" + stationA.getName() + "</stationA>" + "\n" +
                "<stationB>" + stationB.getName() + "</stationB>" + "\n" +
                "<line>" + line.getName() + "</line>" + "\n" +
                "<serialNumber>" + serialNumber + "</serialNumber>" + "\n" +
                "<distance>" + distance + "</distance>" + "\n" +
                "<time>" + time + "</time>" + "\n" +
                "</edge>" + "\n";
    }

    /**
     * 
     * @param gg
     * @param matrixSize
     */
    public void draw(Graphics2D gg, int matrixSize) {
        Graphics2D g2D = (Graphics2D) gg.create();

        int x1 = stationA.getLogX() + stationA.getWidth() / 2;
        int y1 = stationA.getLogY() + stationA.getHeight() / 2;
        int x2 = stationB.getLogX() + stationB.getWidth() / 2;
        int y2 = stationB.getLogY() + stationB.getHeight() / 2;

        if (x1 > x2) {
            int t = x1;
            x1 = x2;
            x2 = t;
            t = y1;
            y1 = y2;
            y2 = t;
        }

        g2D.setColor(line.getColor());
        if (line.getStatus().equals(MetroLine.STATUS_METRO)) {
            float[] dashPattern = {matrixSize, matrixSize * (getEdgesSize() - 1)};
            g2D.setStroke(new BasicStroke(matrixSize * 2, BasicStroke.CAP_BUTT,
                    BasicStroke.JOIN_BEVEL, 10, dashPattern, getEdgeLocation() * matrixSize));
        } else {
            float[] dashPattern = {matrixSize, matrixSize};
            g2D.setStroke(new BasicStroke(matrixSize * 2, BasicStroke.CAP_BUTT,
                    BasicStroke.JOIN_BEVEL, 10, dashPattern, 0));
        }

        g2D.drawLine(x1 * matrixSize, y1 * matrixSize, x2 * matrixSize, y2 * matrixSize);
    }

    /**
     * 
     * @param o
     * @return
     */
    public int compareTo(MetroEdge o) {
        if (line.compareTo(o.line) < 0) {
            return -1;
        } else if (line.compareTo(o.line) > 0) {
            return 1;
        } else {
            if (serialNumber < o.serialNumber) {
                return -1;
            } else if (serialNumber > o.serialNumber) {
                return 1;
            } else {
                return 0;
            }
        }
    }

    public void drawHighLight(Graphics2D g2D, int matrixSize) {
        g2D.setColor(Color.BLACK);
        g2D.setStroke(new BasicStroke(matrixSize));

        int x1 = stationA.getLogX() + stationA.getWidth() / 2;
        int y1 = stationA.getLogY() + stationA.getHeight() / 2;
        int x2 = stationB.getLogX() + stationB.getWidth() / 2;
        int y2 = stationB.getLogY() + stationB.getHeight() / 2;

        g2D.drawLine(x1 * matrixSize, y1 * matrixSize, x2 * matrixSize, y2 * matrixSize);
    }

    public boolean isHighLight() {
        return highLightFlag;
    }

    public void setHighLight(boolean lightFlag) {
        this.highLightFlag = lightFlag;
    }

    public void eUpdate() {
        similarEdges = ElementManager.getInstance().getSimilarEdges(this);
    }
    private MetroStation stationA;
    private MetroStation stationB;
    private MetroLine line;
    private int serialNumber;
    private double distance;
    private double time;
    private boolean language;
    private boolean highLightFlag;
    private ArrayList<MetroEdge> similarEdges;
}
